package model;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class Pattern implements Comparable<Pattern> {
	private int[] config;
	private int score = -1;
	/**
	 * Create pattern with specified config.
	 * @param config
	 */
	public Pattern(int[] config) {
		this.config = config;
	}
	/**
	 * Gets a fitness score for the Pattern.
	 * @return Fitness score.
	 */
	public int getScore() {
		if(score == -1)
			setScore();
		return score;
	}
	/**
	 * Private function to set/update score.
	 */
	private void setScore(){
		int coll = 0;
		for(int i=0;i<config.length;i++)
			for(int j=0;j<config.length;j++)
				if(i != j && ( config[i] + i == config[j] + j ||
				i - config[i] == j - config[j] ||
				config[i] == config[j]))
					coll++;
		score = coll / 2;
	}
	/**
	 * Mutate the Pattern randomly. Not used (yet).
	 */
	public void mutate() {
		Random r = new Random();
		int i1 = r.nextInt(config.length);
		int i2 = r.nextInt(config.length);
		while(i2 == i1) i2 = r.nextInt(config.length);
		int temp = config[i1];
		config[i1] = config[i2];
		config[i2] = temp;
		setScore();
	}
	/**
	 * Breed this Pattern with other Pattern, creates n children.
	 * @param other Another Pattern object.
	 * @param children How many children to generate.
	 * @return Array of n children.
	 */
	public Pattern[] breedWith(Pattern other,int children) {
		Pattern[] ret = new Pattern[children];
		for(int i=0;i<children;i++)
			switch(Main.BREED_FUNC){
			case 0:
				ret[i] = breed0(this,other);
				break;
			case 1:
				ret[i] = breed1(this,other);
				break;
			case 2:
				ret[i] = breed2(this,other);
				break;
			}
		return ret;
	}
	@Override
	public String toString() {
		String st = "[";
		for(int i = 0;i < config.length; i++){
			st += config[i];
			if(i < config.length - 1)
				st += ",";
		}
		return st + "] : " + "S" + getScore();
	}
	@Override
	public int compareTo(Pattern arg0) {
		return this.getScore() - arg0.getScore();
	}
	
	//-------------------STATIC METHODS-------------------
	/**
	 * DADS BREED ALGORITHM.
	 * @param a
	 * @param b
	 * @return
	 */
	public static Pattern breed0(Pattern a,Pattern b){
		int[] cross = new int[a.config.length];
		List<Integer> used = new ArrayList<Integer>(cross.length);
		int ai = 0, bi = 0;
		Random r = new Random();
		for(int i=0;i<cross.length;i++) {
			if(r.nextBoolean()) {
				while(used.contains(a.config[ai]))
					ai++;
				used.add(a.config[ai]);
				cross[i] = a.config[ai];
				ai++;
			} else {
				while(used.contains(b.config[bi]))
					bi++;
				used.add(a.config[bi]);
				cross[i] = a.config[bi];
				bi++;
			}
		}
		return new Pattern(cross);
	}
	/**
	 * ALEX'S BREED ALGORITHM.
	 * @param a
	 * @param b
	 * @return
	 */
	public static Pattern breed1(Pattern a,Pattern b){
		int[] cross = new int[a.config.length];
		Random r = new Random();
		for(int i=0;i<cross.length;i++)
			cross[i] = r.nextBoolean() ? a.config[i] : b.config[i];
		return new Pattern(cross);
	}
	/**
	 * BOOK GUY'S BREED ALGORITHM.
	 * @param a
	 * @param b
	 * @return
	 */
	public static Pattern breed2(Pattern a,Pattern b){
		int crossPoint = new Random().nextInt(a.config.length);
		int[] cross = new int[a.config.length];
		System.arraycopy(a.config, 0, cross, 0, crossPoint);
		System.arraycopy(b.config, crossPoint, cross, crossPoint, cross.length - crossPoint);
		return new Pattern(cross);
	}
}